Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / service / src / commonTest / kotlin / org / meshtastic / core / service / RadioControllerImplTest.kt

Displaying Raw • Download

core/service/src/commonTest/kotlin/org/meshtastic/core/service/RadioControllerImplTest.kt cef12c39ddda652eec872a2ccffa52a2665bd2ec (cef12c39) Text, 18.83 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.service

Tff7b72import T7ee787dev.mokkery.MockMode
Tff7b72import T7ee787dev.mokkery.answering.calls
Tff7b72import T7ee787dev.mokkery.answering.returns
Tff7b72import T7ee787dev.mokkery.every
Tff7b72import T7ee787dev.mokkery.everySuspend
Tff7b72import T7ee787dev.mokkery.matcher.any
Tff7b72import T7ee787dev.mokkery.mock
Tff7b72import T7ee787dev.mokkery.verify
Tff7b72import T7ee787dev.mokkery.verify.VerifyMode.Companion.atLeast
Tff7b72import T7ee787dev.mokkery.verify.VerifyMode.Companion.exactly
Tff7b72import T7ee787dev.mokkery.verifySuspend
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.test.TestScope
Tff7b72import T7ee787kotlinx.coroutines.test.advanceUntilIdle
Tff7b72import T7ee787kotlinx.coroutines.test.runTest
Tff7b72import T7ee787org.meshtastic.core.common.database.DatabaseManager
Tff7b72import T7ee787org.meshtastic.core.model.ConnectionState
Tff7b72import T7ee787org.meshtastic.core.model.DataPacket
Tff7b72import T7ee787org.meshtastic.core.model.Node
Tff7b72import T7ee787org.meshtastic.core.model.NodeAddress
Tff7b72import T7ee787org.meshtastic.core.repository.CommandSender
Tff7b72import T7ee787org.meshtastic.core.repository.MeshDataHandler
Tff7b72import T7ee787org.meshtastic.core.repository.MeshLocationManager
Tff7b72import T7ee787org.meshtastic.core.repository.MeshMessageProcessor
Tff7b72import T7ee787org.meshtastic.core.repository.MeshPrefs
Tff7b72import T7ee787org.meshtastic.core.repository.NodeManager
Tff7b72import T7ee787org.meshtastic.core.repository.NodeRepository
Tff7b72import T7ee787org.meshtastic.core.repository.NotificationManager
Tff7b72import T7ee787org.meshtastic.core.repository.PacketRepository
Tff7b72import T7ee787org.meshtastic.core.repository.PlatformAnalytics
Tff7b72import T7ee787org.meshtastic.core.repository.RadioConfigRepository
Tff7b72import T7ee787org.meshtastic.core.repository.RadioInterfaceService
Tff7b72import T7ee787org.meshtastic.core.repository.ServiceRepository
Tff7b72import T7ee787org.meshtastic.core.repository.UiPrefs
Tff7b72import T7ee787org.meshtastic.proto.AdminMessage
Tff7b72import T7ee787org.meshtastic.proto.ClientNotification
Tff7b72import T7ee787org.meshtastic.proto.Config
Tff7b72import T7ee787org.meshtastic.proto.HamParameters
Tff7b72import T7ee787org.meshtastic.proto.LocalConfig
Tff7b72import T7ee787org.meshtastic.proto.SharedContact
Tff7b72import T7ee787org.meshtastic.proto.User
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertNull
Tff7b72import T7ee787kotlin.test.assertSame
Tff7b72import T7ee787kotlin.test.assertTrue

Tff7b72class T56d364RadioControllerImplTest Tb4b4b4{

Tff7b72private Tff7b72val Te6edf3nodeRepositoryTb4b4b4: Te6edf3NodeRepository Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3commandSenderTb4b4b4: Te6edf3CommandSender Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3nodeManagerTb4b4b4: Te6edf3NodeManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3radioInterfaceServiceTb4b4b4: Te6edf3RadioInterfaceService Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3locationManagerTb4b4b4: Te6edf3MeshLocationManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3packetRepositoryTb4b4b4: Te6edf3PacketRepository Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3dataHandlerTb4b4b4: Te6edf3MeshDataHandler Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3analyticsTb4b4b4: Te6edf3PlatformAnalytics Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3meshPrefsTb4b4b4: Te6edf3MeshPrefs Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3uiPrefsTb4b4b4: Te6edf3UiPrefs Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3databaseManagerTb4b4b4: Te6edf3DatabaseManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3notificationManagerTb4b4b4: Te6edf3NotificationManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3messageProcessorTb4b4b4: Te6edf3MeshMessageProcessor Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3radioConfigRepositoryTb4b4b4: Te6edf3RadioConfigRepository Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)

Tff7b72private Tff7b72val Te6edf3testScope Tff7b72= Te6edf3TestScopeTb4b4b4(Tb4b4b4)

Tff7b72private Tff7b72fun Td2a8ffcreateControllerTb4b4b4(
Te6edf3serviceRepositoryTb4b4b4: Te6edf3ServiceRepository Tff7b72= Te6edf3ServiceRepositoryImplTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3myNodeNumTb4b4b4: Tffa657Int? Tff7b72= T79c0ff1T79c0ff2T79c0ff3T79c0ff4Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3RadioControllerImpl Tb4b4b4{
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3myNodeNum Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Te6edf3myNodeNumTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3meshPrefsTb4b4b4.Te6edf3deviceAddress Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Tff7b72nullTb4b4b4)
Tff7b72return Te6edf3RadioControllerImplTb4b4b4(
Te6edf3serviceRepository Tff7b72= Te6edf3serviceRepositoryTb4b4b4,
Te6edf3nodeRepository Tff7b72= Te6edf3nodeRepositoryTb4b4b4,
Te6edf3commandSender Tff7b72= Te6edf3commandSenderTb4b4b4,
Te6edf3nodeManager Tff7b72= Te6edf3nodeManagerTb4b4b4,
Te6edf3radioInterfaceService Tff7b72= Te6edf3radioInterfaceServiceTb4b4b4,
Te6edf3locationManager Tff7b72= Te6edf3locationManagerTb4b4b4,
Te6edf3packetRepository Tff7b72= Te6edf3lazy Tb4b4b4{ Te6edf3packetRepository Tb4b4b4}Tb4b4b4,
Te6edf3dataHandler Tff7b72= Te6edf3lazy Tb4b4b4{ Te6edf3dataHandler Tb4b4b4}Tb4b4b4,
Te6edf3analytics Tff7b72= Te6edf3analyticsTb4b4b4,
Te6edf3meshPrefs Tff7b72= Te6edf3meshPrefsTb4b4b4,
Te6edf3uiPrefs Tff7b72= Te6edf3uiPrefsTb4b4b4,
Te6edf3databaseManager Tff7b72= Te6edf3databaseManagerTb4b4b4,
Te6edf3notificationManager Tff7b72= Te6edf3notificationManagerTb4b4b4,
Te6edf3messageProcessor Tff7b72= Te6edf3lazy Tb4b4b4{ Te6edf3messageProcessor Tb4b4b4}Tb4b4b4,
Te6edf3radioConfigRepository Tff7b72= Te6edf3radioConfigRepositoryTb4b4b4,
Te6edf3scope Tff7b72= Te6edf3testScopeTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffconnectionStateAndClientNotificationDelegateToServiceRepositoryTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3serviceRepository Tff7b72= Te6edf3ServiceRepositoryImplTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Te6edf3serviceRepository Tff7b72= Te6edf3serviceRepositoryTb4b4b4)
Tff7b72val Te6edf3notification Tff7b72= Te6edf3ClientNotificationTb4b4b4(Tb4b4b4)

Te6edf3assertSameTb4b4b4(Te6edf3serviceRepositoryTb4b4b4.Te6edf3connectionStateTb4b4b4, Te6edf3controllerTb4b4b4.Te6edf3connectionStateTb4b4b4)
Te6edf3assertSameTb4b4b4(Te6edf3serviceRepositoryTb4b4b4.Te6edf3clientNotificationTb4b4b4, Te6edf3controllerTb4b4b4.Te6edf3clientNotificationTb4b4b4)

Te6edf3serviceRepositoryTb4b4b4.Te6edf3setConnectionStateTb4b4b4(Te6edf3ConnectionStateTb4b4b4.Te6edf3ConnectingTb4b4b4)
Te6edf3serviceRepositoryTb4b4b4.Te6edf3setClientNotificationTb4b4b4(Te6edf3notificationTb4b4b4)

Te6edf3assertEqualsTb4b4b4(Te6edf3ConnectionStateTb4b4b4.Te6edf3ConnectingTb4b4b4, Te6edf3controllerTb4b4b4.Te6edf3connectionStateTb4b4b4.Te6edf3valueTb4b4b4)
Te6edf3assertSameTb4b4b4(Te6edf3notificationTb4b4b4, Te6edf3controllerTb4b4b4.Te6edf3clientNotificationTb4b4b4.Te6edf3valueTb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3clearClientNotificationTb4b4b4(Tb4b4b4)

Te6edf3assertNullTb4b4b4(Te6edf3serviceRepositoryTb4b4b4.Te6edf3clientNotificationTb4b4b4.Te6edf3valueTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsendMessageDelegatesToCommandSenderTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Te6edf3myNodeNum Tff7b72= T79c0ff4T79c0ff5T79c0ff6Tb4b4b4)
Tff7b72val Te6edf3packet Tff7b72= Te6edf3DataPacketTb4b4b4(Te6edf3to Tff7b72= Te6edf3NodeAddressTb4b4b4.Te6edf3ID_BROADCASTTb4b4b4, Te6edf3channel Tff7b72= T79c0ff1Tb4b4b4, Te6edf3text Tff7b72= Ta5d6ff"Ta5d6ffpingTa5d6ff"Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3sendMessageTb4b4b4(Te6edf3packetTb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendDataTb4b4b4(Te6edf3packetTb4b4b4) Tb4b4b4}
Te6edf3verifySuspend Tb4b4b4{ Te6edf3dataHandlerTb4b4b4.Te6edf3rememberDataPacketTb4b4b4(Te6edf3packetTb4b4b4, T79c0ff4T79c0ff5T79c0ff6Tb4b4b4, Tff7b72falseTb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsendSharedContactCallsCommandSenderAdminAwaitTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3nodeNum Tff7b72= T79c0ff3T79c0ff2T79c0ff1
Tff7b72val Te6edf3user Tff7b72= Te6edf3UserTb4b4b4(Te6edf3id Tff7b72= Te6edf3NodeAddressTb4b4b4.Te6edf3numToDefaultIdTb4b4b4(Te6edf3nodeNumTb4b4b4)Tb4b4b4, Te6edf3long_name Tff7b72= Ta5d6ff"Ta5d6ffRemote NodeTa5d6ff"Tb4b4b4, Te6edf3short_name Tff7b72= Ta5d6ff"Ta5d6ffRNTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3node Tff7b72= Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= Te6edf3nodeNumTb4b4b4, Te6edf3user Tff7b72= Te6edf3userTb4b4b4, Te6edf3manuallyVerified Tff7b72= Tff7b72trueTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeRepositoryTb4b4b4.Te6edf3getNodeTb4b4b4(Te6edf3NodeAddressTb4b4b4.Te6edf3numToDefaultIdTb4b4b4(Te6edf3nodeNumTb4b4b4)Tb4b4b4) Tb4b4b4} Te6edf3returns Te6edf3node
Te6edf3everySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminAwaitTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4} Te6edf3returns Tff7b72true

Tff7b72val Te6edf3result Tff7b72= Te6edf3controllerTb4b4b4.Te6edf3sendSharedContactTb4b4b4(Te6edf3nodeNumTb4b4b4)

Te6edf3assertTrueTb4b4b4(Te6edf3resultTb4b4b4)
Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminAwaitTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffrequestConfigOperationsDelegateToCommandSenderTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3getOwnerTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff1Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff1Tb4b4b4)
Te6edf3controllerTb4b4b4.Te6edf3getConfigTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff2Tb4b4b4, Te6edf3configType Tff7b72= T79c0ff2Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff3Tb4b4b4)
Te6edf3controllerTb4b4b4.Te6edf3getModuleConfigTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff3Tb4b4b4, Te6edf3moduleConfigType Tff7b72= T79c0ff4Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff5Tb4b4b4)
Te6edf3controllerTb4b4b4.Te6edf3getChannelTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff4Tb4b4b4, Te6edf3index Tff7b72= T79c0ff6Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff7Tb4b4b4)
Te6edf3controllerTb4b4b4.Te6edf3getRingtoneTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff5Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff8Tb4b4b4)
Te6edf3controllerTb4b4b4.Te6edf3getCannedMessagesTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff6Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff9Tb4b4b4)
Te6edf3controllerTb4b4b4.Te6edf3getDeviceConnectionStatusTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff7Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff1T79c0ff0Tb4b4b4)

T8b949e// All delegate to commandSender.sendAdmin
Te6edf3verifySuspendTb4b4b4(Te6edf3atLeastTb4b4b4(T79c0ff7Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffstopProvideLocationDelegatesToLocationManagerTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3stopProvideLocationTb4b4b4(Tb4b4b4)

Te6edf3verify Tb4b4b4{ Te6edf3locationManagerTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsetDeviceAddressSwitchesDatabaseAndTransportTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3meshPrefsTb4b4b4.Te6edf3deviceAddress Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Ta5d6ff"Ta5d6ffold:addrTa5d6ff"Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3setDeviceAddressTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.1Ta5d6ff"Tb4b4b4)
Te6edf3testScopeTb4b4b4.Te6edf3advanceUntilIdleTb4b4b4(Tb4b4b4)

T8b949e// Verify ordering: switchDevice completes before transport reconfiguration
Te6edf3verifySuspend Tb4b4b4{ Te6edf3meshPrefsTb4b4b4.Te6edf3setDeviceAddressTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.1Ta5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3verifySuspend Tb4b4b4{ Te6edf3databaseManagerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.1Ta5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3verify Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3setDeviceAddressTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.1Ta5d6ff"Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsetDeviceAddressSkipsSwitchWhenAddressUnchangedTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3meshPrefsTb4b4b4.Te6edf3deviceAddress Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.1Ta5d6ff"Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3setDeviceAddressTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.1Ta5d6ff"Tb4b4b4)
Te6edf3testScopeTb4b4b4.Te6edf3advanceUntilIdleTb4b4b4(Tb4b4b4)

T8b949e// switchDevice should skip when addresses match, but transport still reconfigures
Te6edf3verify Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3setDeviceAddressTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.1Ta5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3meshPrefsTb4b4b4.Te6edf3setDeviceAddressTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.1Ta5d6ff"Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsendReactionPersistsToDatabaseTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3user Tff7b72= Te6edf3UserTb4b4b4(Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ff!abcd1234Ta5d6ff"Tb4b4b4, Te6edf3long_name Tff7b72= Ta5d6ff"Ta5d6ffTestTa5d6ff"Tb4b4b4, Te6edf3short_name Tff7b72= Ta5d6ff"Ta5d6ffTTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3node Tff7b72= Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff1T79c0ff2T79c0ff3T79c0ff4Tb4b4b4, Te6edf3user Tff7b72= Te6edf3userTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3nodeDBbyNodeNum Tb4b4b4} Te6edf3returns Te6edf3mapOfTb4b4b4(T79c0ff1T79c0ff2T79c0ff3T79c0ff4 Te6edf3to Te6edf3nodeTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3getMyIdTb4b4b4(Tb4b4b4) Tb4b4b4} Te6edf3returns Ta5d6ff"Ta5d6ff!abcd1234Ta5d6ff"

Te6edf3controllerTb4b4b4.Te6edf3sendReactionTb4b4b4(Te6edf3emoji Tff7b72= Ta5d6ff"Ta5d6ff👍Ta5d6ff"Tb4b4b4, Te6edf3replyId Tff7b72= T79c0ff4T79c0ff2Tb4b4b4, Te6edf3contactKey Tff7b72= Ta5d6ff"Ta5d6ff0!dest5678Ta5d6ff"Tb4b4b4)

T8b949e// Reaction must be persisted (not fire-and-forget)
Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendDataTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Te6edf3verifySuspend Tb4b4b4{ Te6edf3packetRepositoryTb4b4b4.Te6edf3insertReactionTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsetFavoriteSendsAdminAndUpdatesStateTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3node Tff7b72= Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff9T79c0ff9Tb4b4b4, Te6edf3user Tff7b72= Te6edf3UserTb4b4b4(Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ff!node99Ta5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3isFavorite Tff7b72= Tff7b72falseTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3nodeDBbyNodeNum Tb4b4b4} Te6edf3returns Te6edf3mapOfTb4b4b4(T79c0ff9T79c0ff9 Te6edf3to Te6edf3nodeTb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3setFavoriteTb4b4b4(T79c0ff9T79c0ff9Tb4b4b4, Te6edf3favorite Tff7b72= Tff7b72trueTb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Te6edf3verify Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3updateNodeTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsetFavoriteIsNoOpWhenAlreadyInRequestedStateTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3node Tff7b72= Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff9T79c0ff9Tb4b4b4, Te6edf3user Tff7b72= Te6edf3UserTb4b4b4(Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ff!node99Ta5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3isFavorite Tff7b72= Tff7b72trueTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3nodeDBbyNodeNum Tb4b4b4} Te6edf3returns Te6edf3mapOfTb4b4b4(T79c0ff9T79c0ff9 Te6edf3to Te6edf3nodeTb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3setFavoriteTb4b4b4(T79c0ff9T79c0ff9Tb4b4b4, Te6edf3favorite Tff7b72= Tff7b72trueTb4b4b4)

Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Te6edf3verifyTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3updateNodeTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsetIgnoredSendsAdminUpdatesStateAndFiltersPacketsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3node Tff7b72= Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff9T79c0ff9Tb4b4b4, Te6edf3user Tff7b72= Te6edf3UserTb4b4b4(Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ff!node99Ta5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3isIgnored Tff7b72= Tff7b72falseTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3nodeDBbyNodeNum Tb4b4b4} Te6edf3returns Te6edf3mapOfTb4b4b4(T79c0ff9T79c0ff9 Te6edf3to Te6edf3nodeTb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3setIgnoredTb4b4b4(T79c0ff9T79c0ff9Tb4b4b4, Te6edf3ignored Tff7b72= Tff7b72trueTb4b4b4)
Te6edf3testScopeTb4b4b4.Te6edf3advanceUntilIdleTb4b4b4(Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Te6edf3verify Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3updateNodeTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Te6edf3verifySuspend Tb4b4b4{ Te6edf3packetRepositoryTb4b4b4.Te6edf3updateFilteredBySenderTb4b4b4(Ta5d6ff"Ta5d6ff!node99Ta5d6ff"Tb4b4b4, Tff7b72trueTb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fftoggleMutedSendsAdminAndUpdatesStateTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3node Tff7b72= Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff9T79c0ff9Tb4b4b4, Te6edf3user Tff7b72= Te6edf3UserTb4b4b4(Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ff!node99Ta5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3isMuted Tff7b72= Tff7b72falseTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3nodeDBbyNodeNum Tb4b4b4} Te6edf3returns Te6edf3mapOfTb4b4b4(T79c0ff9T79c0ff9 Te6edf3to Te6edf3nodeTb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3toggleMutedTb4b4b4(T79c0ff9T79c0ff9Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Te6edf3verify Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3updateNodeTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffnodeManagementReturnsEarlyWhenMyNodeNumIsNullTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Te6edf3myNodeNum Tff7b72= Tff7b72nullTb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3setFavoriteTb4b4b4(T79c0ff9T79c0ff9Tb4b4b4, Te6edf3favorite Tff7b72= Tff7b72trueTb4b4b4)
Te6edf3controllerTb4b4b4.Te6edf3setIgnoredTb4b4b4(T79c0ff9T79c0ff9Tb4b4b4, Te6edf3ignored Tff7b72= Tff7b72trueTb4b4b4)
Te6edf3controllerTb4b4b4.Te6edf3toggleMutedTb4b4b4(T79c0ff9T79c0ff9Tb4b4b4)

Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffremoveByNodenumAlwaysRemovesLocallyAndSendsAdminWhenConnectedTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3removeByNodenumTb4b4b4(Te6edf3packetId Tff7b72= T79c0ff1Tb4b4b4, Te6edf3nodeNum Tff7b72= T79c0ff5T79c0ff5Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3removeByNodenumTb4b4b4(T79c0ff5T79c0ff5Tb4b4b4) Tb4b4b4}
Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffremoveByNodenumRemovesLocallyEvenWhenDisconnectedTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Te6edf3myNodeNum Tff7b72= Tff7b72nullTb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3removeByNodenumTb4b4b4(Te6edf3packetId Tff7b72= T79c0ff1Tb4b4b4, Te6edf3nodeNum Tff7b72= T79c0ff5T79c0ff5Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3removeByNodenumTb4b4b4(T79c0ff5T79c0ff5Tb4b4b4) Tb4b4b4}
T8b949e// No admin message sent when disconnected
Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffrebootSendsAdminMessageWithDelayTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3rebootTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff1Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff7Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffshutdownSendsAdminMessageTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3shutdownTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff1Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff8Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8fffactoryResetSendsAdminMessageTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3factoryResetTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff1Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff9Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffnodedbResetSendsAdminMessageTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3nodedbResetTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff1Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff1T79c0ff0Tb4b4b4, Te6edf3preserveFavorites Tff7b72= Tff7b72trueTb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsetTimeSendsAdminMessageWithCurrentEpochSecondsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)

Tff7b72var Te6edf3sentMessageTb4b4b4: Te6edf3AdminMessage? Tff7b72= Tff7b72null
Te6edf3everySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4} Te6edf3calls
Tb4b4b4{
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffUNCHECKED_CASTTa5d6ff"Tb4b4b4)
Te6edf3sentMessage Tff7b72= Tb4b4b4(Tffa657itTb4b4b4.Te6edf3argsTff7b72[T79c0ff3Tff7b72] Tff7b72as Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3AdminMessageTb4b4b4)Tb4b4b4(Tb4b4b4)
Tb4b4b4}

Te6edf3controllerTb4b4b4.Te6edf3setTimeTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff1Tb4b4b4, Te6edf3packetId Tff7b72= T79c0ff1T79c0ff1Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
T8b949e// The phone's current time is sent; assert it is populated and a plausible recent epoch (after 2020).
Tff7b72val Te6edf3setTime Tff7b72= Te6edf3sentMessageTff7b72?.Te6edf3set_time_only
Te6edf3assertTrueTb4b4b4(Te6edf3setTime Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3setTime Tff7b72> T79c0ff1Te6edf3_577_836_800Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffrefreshMetadataSendsAdminWithWantResponseTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3refreshMetadataTb4b4b4(Te6edf3destNum Tff7b72= T79c0ff1T79c0ff0T79c0ff1Tb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffimportContactSendsAdminAndUpdatesNodeManagerTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Tb4b4b4)
T8b949e// A QR-scanned contact arrives with manually_verified = false (proto default).
Tff7b72val Te6edf3contact Tff7b72= Te6edf3SharedContactTb4b4b4(Te6edf3node_num Tff7b72= T79c0ff4T79c0ff2Tb4b4b4, Te6edf3user Tff7b72= Te6edf3UserTb4b4b4(Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ff!0000002aTa5d6ff"Tb4b4b4, Te6edf3long_name Tff7b72= Ta5d6ff"Ta5d6ffTestTa5d6ff"Tb4b4b4)Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3importContactTb4b4b4(Te6edf3contactTb4b4b4)

Te6edf3verifySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
T8b949e// Importing is an act of manual verification, so the node is recorded as verified.
Te6edf3verify Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3handleReceivedUserTb4b4b4(T79c0ff4T79c0ff2Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Tff7b72trueTb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsetHamModeSendsAdminWithEchoedLoraValuesAndUpdatesUserTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Te6edf3myNodeNum Tff7b72= T79c0ff1T79c0ff2T79c0ff3Tb4b4b4)
Tff7b72val Te6edf3existingUser Tff7b72= Te6edf3UserTb4b4b4(Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ff!0000007bTa5d6ff"Tb4b4b4, Te6edf3long_name Tff7b72= Ta5d6ff"Ta5d6ffOld NameTa5d6ff"Tb4b4b4, Te6edf3short_name Tff7b72= Ta5d6ff"Ta5d6ffOLDTa5d6ff"Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3nodeDBbyNodeNum Tb4b4b4} Te6edf3returns Te6edf3mapOfTb4b4b4(T79c0ff1T79c0ff2T79c0ff3 Te6edf3to Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff1T79c0ff2T79c0ff3Tb4b4b4, Te6edf3user Tff7b72= Te6edf3existingUserTb4b4b4)Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3radioConfigRepositoryTb4b4b4.Te6edf3localConfigFlow Tb4b4b4} Te6edf3returns
Te6edf3MutableStateFlowTb4b4b4(Te6edf3LocalConfigTb4b4b4(Te6edf3lora Tff7b72= Te6edf3ConfigTb4b4b4.Te6edf3LoRaConfigTb4b4b4(Te6edf3tx_power Tff7b72= T79c0ff2T79c0ff0Tb4b4b4, Te6edf3override_frequency Tff7b72= T79c0ff9T79c0ff1T79c0ff5.5fTb4b4b4)Tb4b4b4)Tb4b4b4)

Tff7b72var Te6edf3sentMessageTb4b4b4: Te6edf3AdminMessage? Tff7b72= Tff7b72null
Te6edf3everySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4} Te6edf3calls
Tb4b4b4{
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffUNCHECKED_CASTTa5d6ff"Tb4b4b4)
Te6edf3sentMessage Tff7b72= Tb4b4b4(Tffa657itTb4b4b4.Te6edf3argsTff7b72[T79c0ff3Tff7b72] Tff7b72as Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3AdminMessageTb4b4b4)Tb4b4b4(Tb4b4b4)
Tb4b4b4}

Te6edf3controllerTb4b4b4.Te6edf3setHamModeTb4b4b4(T79c0ff1T79c0ff2T79c0ff3Tb4b4b4, Te6edf3HamParametersTb4b4b4(Te6edf3call_sign Tff7b72= Ta5d6ff"Ta5d6ffKK7ABCTa5d6ff"Tb4b4b4, Te6edf3short_name Tff7b72= Ta5d6ff"Ta5d6ffKK7ATa5d6ff"Tb4b4b4)Tb4b4b4, T79c0ff4T79c0ff2Tb4b4b4)

Tff7b72val Te6edf3ham Tff7b72= Te6edf3sentMessageTff7b72?.Te6edf3set_ham_mode
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffKK7ABCTa5d6ff"Tb4b4b4, Te6edf3hamTff7b72?.Te6edf3call_signTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffKK7ATa5d6ff"Tb4b4b4, Te6edf3hamTff7b72?.Te6edf3short_nameTb4b4b4)
T8b949e// Current LoRa values are echoed so a re-send never wipes the node's overrides.
Te6edf3assertEqualsTb4b4b4(T79c0ff2T79c0ff0Tb4b4b4, Te6edf3hamTff7b72?.Te6edf3tx_powerTb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff9T79c0ff1T79c0ff5.5fTb4b4b4, Te6edf3hamTff7b72?.Te6edf3frequencyTb4b4b4)
Te6edf3verify Tb4b4b4{
Te6edf3nodeManagerTb4b4b4.Te6edf3handleReceivedUserTb4b4b4(
T79c0ff1T79c0ff2T79c0ff3Tb4b4b4,
Te6edf3existingUserTb4b4b4.Te6edf3copyTb4b4b4(Te6edf3long_name Tff7b72= Ta5d6ff"Ta5d6ffKK7ABCTa5d6ff"Tb4b4b4, Te6edf3short_name Tff7b72= Ta5d6ff"Ta5d6ffKK7ATa5d6ff"Tb4b4b4, Te6edf3is_licensed Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4,
T79c0ff0Tb4b4b4,
Tff7b72falseTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsetHamModeWithNoCachedLoraConfigSendsProtoDefaultsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Te6edf3myNodeNum Tff7b72= T79c0ff1T79c0ff2T79c0ff3Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3nodeDBbyNodeNum Tb4b4b4} Te6edf3returns Te6edf3emptyMapTb4b4b4(Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3radioConfigRepositoryTb4b4b4.Te6edf3localConfigFlow Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Te6edf3LocalConfigTb4b4b4(Tb4b4b4)Tb4b4b4)

Tff7b72var Te6edf3sentMessageTb4b4b4: Te6edf3AdminMessage? Tff7b72= Tff7b72null
Te6edf3everySuspend Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4} Te6edf3calls
Tb4b4b4{
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffUNCHECKED_CASTTa5d6ff"Tb4b4b4)
Te6edf3sentMessage Tff7b72= Tb4b4b4(Tffa657itTb4b4b4.Te6edf3argsTff7b72[T79c0ff3Tff7b72] Tff7b72as Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3AdminMessageTb4b4b4)Tb4b4b4(Tb4b4b4)
Tb4b4b4}

Te6edf3controllerTb4b4b4.Te6edf3setHamModeTb4b4b4(T79c0ff1T79c0ff2T79c0ff3Tb4b4b4, Te6edf3HamParametersTb4b4b4(Te6edf3call_sign Tff7b72= Ta5d6ff"Ta5d6ffKK7ABCTa5d6ff"Tb4b4b4, Te6edf3short_name Tff7b72= Ta5d6ff"Ta5d6ffKK7ATa5d6ff"Tb4b4b4)Tb4b4b4, T79c0ff4T79c0ff2Tb4b4b4)

Tff7b72val Te6edf3ham Tff7b72= Te6edf3sentMessageTff7b72?.Te6edf3set_ham_mode
Te6edf3assertEqualsTb4b4b4(T79c0ff0Tb4b4b4, Te6edf3hamTff7b72?.Te6edf3tx_powerTb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff0fTb4b4b4, Te6edf3hamTff7b72?.Te6edf3frequencyTb4b4b4)
T8b949e// Unknown node: the optimistic update is built on a default User.
Te6edf3verify Tb4b4b4{
Te6edf3nodeManagerTb4b4b4.Te6edf3handleReceivedUserTb4b4b4(
T79c0ff1T79c0ff2T79c0ff3Tb4b4b4,
Te6edf3UserTb4b4b4(Te6edf3long_name Tff7b72= Ta5d6ff"Ta5d6ffKK7ABCTa5d6ff"Tb4b4b4, Te6edf3short_name Tff7b72= Ta5d6ff"Ta5d6ffKK7ATa5d6ff"Tb4b4b4, Te6edf3is_licensed Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4,
T79c0ff0Tb4b4b4,
Tff7b72falseTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffsetHamModeIgnoresRemoteDestinationsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Te6edf3myNodeNum Tff7b72= T79c0ff1T79c0ff2T79c0ff3Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3setHamModeTb4b4b4(T79c0ff4T79c0ff5T79c0ff6Tb4b4b4, Te6edf3HamParametersTb4b4b4(Te6edf3call_sign Tff7b72= Ta5d6ff"Ta5d6ffKK7ABCTa5d6ff"Tb4b4b4, Te6edf3short_name Tff7b72= Ta5d6ff"Ta5d6ffKK7ATa5d6ff"Tb4b4b4)Tb4b4b4, T79c0ff4T79c0ff2Tb4b4b4)

Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Te6edf3verifyTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3handleReceivedUserTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffimportContactReturnsEarlyWhenDisconnectedTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3controller Tff7b72= Te6edf3createControllerTb4b4b4(Te6edf3myNodeNum Tff7b72= Tff7b72nullTb4b4b4)
Tff7b72val Te6edf3contact Tff7b72= Te6edf3SharedContactTb4b4b4(Te6edf3node_num Tff7b72= T79c0ff4T79c0ff2Tb4b4b4, Te6edf3user Tff7b72= Te6edf3UserTb4b4b4(Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ff!0000002aTa5d6ff"Tb4b4b4)Tb4b4b4)

Te6edf3controllerTb4b4b4.Te6edf3importContactTb4b4b4(Te6edf3contactTb4b4b4)

Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3commandSenderTb4b4b4.Te6edf3sendAdminTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.16s